"""
   This is the part of the code that interfaces with Entangled. Ideally,
   you'll be getting everything you need from the UI in this code to talk
   to the DHT, regardless of which UI it is. Then, you'll return what you
   get in some standardized way.

   The uiClass passed to the constructor is the instance of whichever user
   interface class is communicating with the user. All of them are required
   by their AbstractUI superclass to have some functions for dealing with
   the deferred objects Entangled will produce:

   searchResult(result)
   filePublished(result)
   downloadFile(result)

   Assuming your deferred object is called df, you'll set these with:
   
   df.addCallback(searchResult)

   That way, the UI will deal with whatever the Entangled network produces.
   AWESOME.
"""

import os
import entangled.node
import hashlib
from twisted.internet import defer
import twisted.internet.reactor
import socket
from entangled.kademlia.datastore import SQLiteDataStore
import thread

from listen import Listener

import fileshareutils

class EntangledNetworkLayer:
   #These things go into the DHT as part of the value in the key/value pair
   myIP = "PlaceholderIP"
   listenPort = 0

   node = None
   uiClass = None

   listener = None

   '''Some clarification:
      uiClass:   The class instance responsible for the user interface
      myPort:    The port that the Entangled network will operate from.
                 This is distinct from the port the listener listens on!
      listenPort: The port the listener operates from. This is put in the
                  DHT as part of the value in the key/value pair.
      connectIP: The IP address of another Entangled node we're connecting to
      connectPort: The corresponding port
   '''
   def __init__(self, uiClass, myPort, listenPort, connectIP, connectPort):
      self.uiClass = uiClass
      self.listenPort = listenPort
      if connectIP != "": connectIP= socket.gethostbyname(connectIP)
      print connectIP

      '''Set up the listener, including a super awful hacky way of getting the IP!'''
      listener = Listener(listenPort)
      s = socket.socket()
      s.connect(('google.com', 80))
      self.myIP = s.getsockname()[0]
      del s
      thread.start_new_thread(listener.run, ())

      '''Set up the node'''
      dataStore = None#SQLiteDataStore(dbFile="/tmp/fileshare%s.db"%myPort)
      self.node = entangled.node.EntangledNode(udpPort=myPort,dataStore = dataStore)
      if connectIP == "":
         print "Connection-less node started"
         ips = None
      else:
         print "Connecting to " + connectIP + " on port " + str(connectPort)
         ips = [(connectIP, connectPort)]
      self.node.joinNetwork(ips)

      '''Set up the main UI and start the reactor'''
      thread.start_new_thread(self.uiClass.run, (self,))
      twisted.internet.reactor.run()

   def error(self, error):
      print error

   #Share a single file 
   def share(self, sharedFile):
      filename = os.path.split(sharedFile)[1]
      valString = self.myIP+","+str(self.listenPort)+","+sharedFile+","+fileshareutils.filehash(sharedFile)

      #df = self.node.publishData(filename, self.myIP+","+str(self.listenPort)+","+sharedFile+","+str(fileshareutils.filehash(sharedFile)))
      df = self.node.publishData(filename, valString)
      #We don't really need to deal with the deferred object, forget the callback
      #df.addErrback(self.error)
      

   #Perform a search on the list of keywords
   def search(self, keywords):
      if len(keywords) == 0:
         return
      df = self.node.searchForKeywords(keywords)
      df.addCallback(self.displaySearchResult)
      df.addErrback(self.error)

   def displaySearchResult(self, searchResults):
      for i in searchResults:
         h = hashlib.sha1()
         h.update(i)
         df = self.node.iterativeFindValue(h.digest())
         df.addCallback(self.uiClass.do_searchDisplay)
         df.addErrback(self.error)

   def get(self, key):
      h = hashlib.sha1()
      h.update(key)
      df = self.node.iterativeFindValue(h.digest())
      df.addCallback(self.uiClass.download)
      df.addErrback(self.error)
      
